'''
Created on Jul 9, 2011

@author: Cihat Basol
'''       
        
        
from PyQt4.QtGui import *
from PyQt4.QtCore import SIGNAL, Qt, QModelIndex, QVariant, QAbstractTableModel

from dexen.system.client import ui, db
from dexen.system.common.meta import ScoreMeta
from dexen.system.common.base import Individual
from dexen.system.client.ui.widgets.table_view import TableView
from dexen.system.client.ui.dialogs.export_dlg import ExportDialog
from dexen.system.client.ui.dialogs.archive_dlg import ArchiveDialog
from dexen.system.client.controller.archive_syncher import ArchiveSyncher


svr_proxy = ui.svr_proxy

class IndividualModel(QAbstractTableModel):
    def __init__(self, inds, scores, parent=None):
        super(IndividualModel, self).__init__(parent)
        self._header_items = []
        self._content_items = []
        self._content_str = ""
        self.inds = inds
        self.scores = scores # List of ScoreMeta
        self._set_header()
        self._set_content()

    def rowCount(self, index=QModelIndex()):
        return len(self._content_items)
    
    def columnCount(self, index=QModelIndex()):
        if len(self._content_items) == 0:
            return 0
        return len(self._content_items[0])

    def headerData(self, section, orientation, role=Qt.DisplayRole):
        if role != Qt.DisplayRole or self._header_items == []:
            return QVariant()
        if orientation == Qt.Horizontal:
            return QVariant(self._header_items[section])
        if orientation == Qt.Vertical:
            return QVariant(section + 1)
    
    def data(self, index, role=Qt.DisplayRole):
        assert isinstance(index, QModelIndex)
        if not index.isValid(): return QVariant()
        
        item = self._content_items[index.row()][2]
                
        if role == Qt.BackgroundRole:
            if str(item) == "None":
                return QColor(Qt.gray)
            return QColor(0x4E, 0x92, 0x58, 128)
        
        if role == Qt.ForegroundRole:
            if str(item) == "None":
                return QColor(Qt.black)
            return QColor(Qt.black)
        
        if role == Qt.DisplayRole:
            return QVariant(self._content_items[index.row()][index.column()])
    
    def get_log(self):
        return self._content_str   

    def _set_header(self):
        """Id, Birth time, Death time, <list of scores>, Rank"""
        self._header_items = Individual._get_header().split(",")
        
        for score_meta in self.scores:
            assert isinstance(score_meta, ScoreMeta)
            self._header_items.append(score_meta.name)
        self._header_items.append("Rank")
        
        assert self.columnCount() == 0
        self.beginInsertColumns(QModelIndex(), 0, len(self._header_items) - 1)
        self.endInsertColumns()

        self._content_str = ",".join(self._header_items) + "\n"

    
    def _set_content(self):        
        self.beginInsertRows(QModelIndex(), 0, len(self.inds) - 1)

        for ind in self.inds:
            items_str = ""
            items = []
            
            assert isinstance(ind, Individual)
            log = ind._get_pareto_log()
            items_str += log 
            items.extend(log.split(","))
            
            for score_meta in self.scores:
                if hasattr(ind, score_meta.name):
                    score_val = getattr(ind, score_meta.name)
                    items_str += ",%f"%(score_val)
                else:
                    score_val = "None"
                    items_str += ",None"
                items.append(score_val)
                
            items_str += ",None\n"
            items.append("None")
                                
            self._content_items.append(items)
            #self._content_str.join(items_str)
            self._content_str += items_str
            #TODO: Watch out for \n for the content_str
            
        self.endInsertRows()


class SortFilterProxyModel(QSortFilterProxyModel):
    def __init__(self, parent=None):
        QSortFilterProxyModel.__init__(self, parent)
    
    def lessThan(self, left, right):
        assert isinstance(left, QModelIndex)
        assert isinstance(right, QModelIndex)
        
        leftData = self.sourceModel().data(left)
        rightData = self.sourceModel().data(right)
        
        assert isinstance(leftData, QVariant)
        assert isinstance(rightData, QVariant)
        
        if left.column() == 0 or left.column() == 1:
            return leftData.toInt() < rightData.toInt()
        
        model = self.sourceModel()
        assert isinstance(model, IndividualModel)
        
        # Death time can be None which means, it is not dead
        # Treat it like infinity
        if left.column() == 2 or \
           left.column() == model.columnCount() - 1: # last column rank column
            if rightData.toString() == "None":
                return True
            if leftData.toString() == "None":
                return False
            return leftData.toInt() < rightData.toInt()
        
        # score columns
        return leftData.toDouble() < rightData.toDouble()

    def headerData(self, section, orientation, role=Qt.DisplayRole):
        if role != Qt.DisplayRole:
            return QVariant()
        if orientation == Qt.Horizontal:
            return self.sourceModel().headerData(section, orientation, role)
        if orientation == Qt.Vertical:
            return QVariant(section + 1)

    def get_content(self):
        model = self.sourceModel()
        assert isinstance(model, IndividualModel)
        content = ",".join(model._header_items) + "\n"
        for i in xrange(self.rowCount()):
            line = ""
            cols = self.columnCount()
            for j in xrange(cols - 1):
                variant = self.data(self.index(i,j))
                assert isinstance(variant, QVariant)
                line += str(variant.toString()) + ","
            line += str(self.data(self.index(i, cols-1)).toString())
            content += line + "\n"
        return content


class IndsPage(QWidget):       
    def __init__(self):
        super(IndsPage, self).__init__()
        self.curJobInfo = None
        self.models = {} #job_id: BaseTableModel
        self._create_widgets()

    def _create_widgets(self):
        # Create widgets
        self.tableView = TableView()
        self.exportBtn = QPushButton("Export")
        self.downloadBtn = QPushButton("Synch all archives")
        
        # Configure widgets
        self.tableView.resizeColumnsToContents()
        self.exportBtn.setToolTip("Exports the table into a csv file")
        self.downloadBtn.setToolTip("Downloads archives into the client database")
        
        # Layout widgets
        hLayout = QHBoxLayout()
        hLayout.addStretch()
        hLayout.addWidget(self.downloadBtn)
        hLayout.addWidget(self.exportBtn)
        
        layout = QVBoxLayout()       
        layout.addLayout(hLayout)
        layout.addWidget(self.tableView)
        self.setLayout(layout)

        # Configure the signal/slot connections
        self.connect(self.exportBtn, SIGNAL("clicked()"), self.on_exportBtn_clicked)
        self.connect(self.downloadBtn, SIGNAL("clicked()"), self.on_downloadBtn_clicked)
        self.connect(self.tableView, SIGNAL("doubleClicked(const QModelIndex&)"), self.on_doubleClicked)
    
    def cur_model(self):
        proxy_model = self.tableView.model()
        assert isinstance(proxy_model, SortFilterProxyModel)
        return proxy_model.sourceModel()
    
    def on_downloadBtn_clicked(self):
        model = self.cur_model()
        assert isinstance(model, IndividualModel)
        progress = QProgressDialog("Synching archived items...", "Stop synch", 
                                   0, model.rowCount(), self)
        progress.setWindowTitle("Synch Archive")
        progress.setWindowModality(Qt.WindowModal)
        progress.show()

        archive_syncher = ArchiveSyncher(ui.job_info)
        for row in xrange(model.rowCount()):
            progress.setValue(row)
            ind_id = model.index(row, 0).data().toInt()[0]
            if progress.wasCanceled():
                break
            archive_syncher.synch(ind_id)
        progress.setValue(model.rowCount())
    
    def on_doubleClicked(self, index):
        assert isinstance(index, QModelIndex)
        model = self.tableView.model()
        assert isinstance(model, SortFilterProxyModel)
        index = model.index(index.row(), 0)
        ind_id = index.data().toInt()[0]
        #print "Indivdual id: ", ind_id, "clicked"
        dlg = ArchiveDialog(ind_id, self)
        ret = dlg.exec_()
        
    def on_exportBtn_clicked(self):
        model = self.tableView.model()
        assert isinstance(model, SortFilterProxyModel)
        content = model.get_content()
        dlg = ExportDialog(self)
        ret_val = dlg.exec_()
        if ret_val == QDialog.Accepted:
            file_path = dlg.get_file_path()
            f = open(file_path, "wb")
            f.write(content)
            f.close()

    def on_cur_job_changed(self, job_info):
        self.curJobInfo = job_info
        if self.models.has_key(job_info):
            model = SortFilterProxyModel()
            model.setSourceModel(self.models[job_info])
            self.tableView.setModel(model)

    def on_inds_initialized(self, job_info, inds):
        self.on_inds_updated(job_info, inds)
    
    def on_inds_updated(self, job_info, inds):
        assert isinstance(job_info, db.JobInfo)
        scores = job_info.get_scores()
        model = IndividualModel(inds, scores)
        
        # Save the fresh model
        self.models[job_info] = model
        
        model = SortFilterProxyModel()
        model.setSourceModel(self.models[job_info])
        
        # Update the view based on the refreshed model
        self.tableView.setModel(model)
        self.tableView.resizeColumnsToContents()
    

